﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Annotations;
using System.Windows.Annotations.Storage;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Xml;
using HeritageAcademy.Models.ViewModels;
using HeritageAcademy.Models.DataModels;

namespace HeritageAcademy.Library.Controls
{
    /// <summary>
    /// The view for the reading experience. This control expects an object of type
    /// BookViewModel as it's DataContext.
    /// </summary>
    public sealed partial class ReadingView
    {
        #region Fields 
        
        readonly SolidColorBrush _highlightBrush = new SolidColorBrush(Color.FromArgb(0x40, 0xFF, 0xC6, 0x00));
        bool _isBookMarkFromMetadata;
        bool _isLoadingVisible = false;
        List<Annotation> _selectedAnnotationCache;
        AnnotationService _service;
        AnnotationStore _store;
        CourseViewModel _viewModel;
        
        #endregion Fields 

        #region Ctor 
        public ReadingView()
        {
            InitializeComponent();
            AnnotationTools.Visibility = Visibility.Collapsed;
            BookViewer.ChapterChanged += BookViewer_ChapterChanged;
            BookViewer.PageChanged += BookViewer_PageChanged;

            // register for selection changed bubbling events
            AddHandler(FlowDocumentPageViewerLight.SelectionChangedEvent,
                       new RoutedEventHandler(BookViewer_SelectionChanged));

            // get the datacontext changes
            DataContextChanged += ReadingView_DataContextChanged;
        }
        #endregion

        #region Event Handlers 
        
        private void BookViewer_BookLoadingProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.ProgressPercentage < 100)
            {
                if (!_isLoadingVisible)
                    LoadingIndicator.Visibility = Visibility.Visible;

                PercentLoadedTextBlock.Text = e.ProgressPercentage + "%";
            }
            else
                LoadingIndicator.Visibility = Visibility.Collapsed;
        }

        private void BookViewer_ChapterChanged(object sender, RoutedEventArgs e)
        {
            var book = DataContext as CourseViewModel;

            if (book == null || BookViewer.CurrentChapter == null) return;
            if (_service != null && _service.IsEnabled)
                _service.Disable();

            _store = book.GetAnnotationStoreForChapter(BookViewer.CurrentChapter);
            _service = new AnnotationService(BookViewer.PageViewer);

            if (_service != null & _store != null)
                _service.Enable(_store);
        }

        private void BookViewer_PageChanged(object sender, EventArgs e)
        {
            if (_viewModel == null) return;
            if (BookViewer == null) return;

            // look to see if we have any bookmarks on the new page
            var viewer = BookViewer.PageViewer;

            if (viewer == null || viewer.Document == null) return;

            var p = (viewer.Document).DocumentPaginator as DynamicDocumentPaginator;

            // set user metadata
            _viewModel.Model.UserMetadata.LastAccess = DateTime.Now;

            // update the user metadata with the current page 
            UpdateMetadataWithCurrentLocation();

            // Look for bookmarks on the current page
            _isBookMarkFromMetadata = true;
            var hasBookmark = CheckForBookmarksOnCurrentPage();
            CurrentPageHasBookmark = hasBookmark;
            _isBookMarkFromMetadata = false;
        }

        private void BookViewer_SelectionChanged(object sender, RoutedEventArgs e)
        {
            _selectedAnnotationCache = null;

            if (!BookViewer.PageViewer.Selection.IsEmpty)
            {
                AnnotationTools.Visibility = Visibility.Visible;
                var r = BookViewer.PageViewer.Selection.Start.GetCharacterRect(LogicalDirection.Forward);

                var t = double.IsInfinity(r.Top) ? 0 : r.Top;
                Canvas.SetTop(AnnotationTools, Math.Round(t, 0));

                var selected = GetSelectedAnnotations();

                DeleteButton.Visibility = selected.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
            }
            else
            {
                AnnotationTools.Visibility = Visibility.Collapsed;
            }
        }

        private void CopyButton_Click(object sender, RoutedEventArgs e)
        {
            if (_service != null && _service.IsEnabled)
                Clipboard.SetText(BookViewer.GetSelectedText());
        }

        private void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            if (_service == null || !_service.IsEnabled) return;
            var selected = GetSelectedAnnotations();
            if (selected.Count <= 0) return;
            foreach (var a in selected)
                _store.DeleteAnnotation(a.Id);
            _viewModel.RefreshBookmarks();
        }

        private void HighlightButton_Click(object sender, RoutedEventArgs e)
        {
            if (_service == null || !_service.IsEnabled) return;
            var text = BookViewer.GetSelectedText();

            var lastWasWhiteSpace = true;
            var newText = "";

            foreach (var c in text)
            {
                if (char.IsWhiteSpace(c))
                {
                    if (!lastWasWhiteSpace) newText = newText + " ";
                    lastWasWhiteSpace = true;
                }
                else
                {
                    lastWasWhiteSpace = false;
                    newText = newText + c;
                }
            }

            newText = newText.Substring(0, Math.Min(newText.Length - 1, 100));

            var a = AnnotationHelper.CreateHighlightForSelection(_service, "", _highlightBrush);
            var doc = new XmlDocument();
            var res = new AnnotationResource("Location");

            res.Contents.Add(BookViewer.CurrentLocation.ToXml(doc, newText));
            if (a != null) a.Cargos.Add(res);
            _viewModel.RefreshBookmarks();
        }

        private void NextPageButton_Click(object sender, RoutedEventArgs e)
        {
            BookViewer.NextPage();
        }

        private void PreviousPageButton_Click(object sender, RoutedEventArgs e)
        {
            BookViewer.PreviousPage();
        }

        private void ReadingView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            // Notify book is unloaded
            if (_viewModel != null)
                _viewModel.NotifyUnload();

            _viewModel = e.NewValue as CourseViewModel;

            if (_viewModel != null)
                _viewModel.CurrentLocation = _viewModel.Model.UserMetadata.LastReadLocation;
        }
        
        #endregion Event Handlers 

        #region Private Methods 

        private void UpdateMetadataWithCurrentLocation()
        {
            if (_viewModel == null) return;

            var currentLocation = BookViewer.CurrentLocation;
            var furthestLocation = _viewModel.Model.UserMetadata.FurthestReadLocation;

            if (currentLocation == null) return;

            if ((furthestLocation == null) ||
                (currentLocation.Chapter > furthestLocation.Chapter) ||
                (currentLocation.Chapter == furthestLocation.Chapter &&
                 currentLocation.ChapterLocation > furthestLocation.ChapterLocation))
            {
                _viewModel.Model.UserMetadata.FurthestReadLocation = currentLocation;
            }

            _viewModel.Model.UserMetadata.LastReadLocation = currentLocation;
        }

        private bool CheckForBookmarksOnCurrentPage()
        {
            return _viewModel != null && _viewModel.Model.UserMetadata.Bookmarks
                                .Select(location => BookViewer.IsLocationOnCurrentPage(location))
                                .Any(isBookmarkOnPage => isBookmarkOnPage);
        }

        private List<Annotation> GetAnnotationsForTextRange(TextPointer start, TextPointer end)
        {
            var annotations = new List<Annotation>();

            if (_store != null)
            {
                foreach (var a in _store.GetAnnotations())
                {
                    var info = AnnotationHelper.GetAnchorInfo(_service, a);
                    var resolvedAnchor = info.ResolvedAnchor as TextAnchor;
                    if (resolvedAnchor == null) continue;
                    var startNote = (TextPointer) resolvedAnchor.BoundingStart;
                    var endNote = (TextPointer) resolvedAnchor.BoundingEnd;

                    // selection contains the entire note
                    if (start.CompareTo(startNote) >= 0 && endNote.CompareTo(end) >= 0)
                    {
                        annotations.Add(a);
                        continue;
                    }

                    if (start.CompareTo(startNote) <= 0 && endNote.CompareTo(end) <= 0)
                    {
                        annotations.Add(a);
                        continue;
                    }

                        // selection begin is before the note begin and select end is after note begin
                    if (start.CompareTo(startNote) <= 0 && end.CompareTo(startNote) > 0 &&
                        end.CompareTo(endNote) <= 0)
                    {
                        annotations.Add(a);
                        continue;
                    }

                    // selection begin is before not end and select end is after note end
                    if (start.CompareTo(startNote) <= 0 || end.CompareTo(endNote) >= 0 || end.CompareTo(endNote) <= 0)
                        continue;
                    annotations.Add(a);
                    continue;
                }
            }

            return annotations;
        }

        private List<Annotation> GetSelectedAnnotations()
        {
            if (_selectedAnnotationCache == null)
            {
                var selected = new List<Annotation>();

                if (_store != null)
                {
                    foreach (var a in _store.GetAnnotations())
                    {
                        var info = AnnotationHelper.GetAnchorInfo(_service, a);
                        var resolvedAnchor = info.ResolvedAnchor as TextAnchor;

                        if (resolvedAnchor == null) continue;
                        var startNote = (TextPointer) resolvedAnchor.BoundingStart;
                        var endNote = (TextPointer) resolvedAnchor.BoundingEnd;

                        var startSelect = BookViewer.PageViewer.Selection.Start;
                        var endSelect = BookViewer.PageViewer.Selection.End;

                        var isSelectionWithin = false;

                        // selection contains the entire note
                        if (startSelect.CompareTo(startNote) >= 0 && endNote.CompareTo(endSelect) >= 0)
                            isSelectionWithin = true;

                            // selection is all within the note and doesn't overlap on either side
                        else if (startSelect.CompareTo(startNote) <= 0 && endNote.CompareTo(endSelect) <= 0)
                            isSelectionWithin = true;

                            // selection overlaps beginning of note
                        else if (startSelect.CompareTo(startNote) <= 0 && endSelect.CompareTo(startNote) > 0 &&
                                 endSelect.CompareTo(endNote) <= 0) isSelectionWithin = true;

                            // selection overlaps end of note
                        else if (startSelect.CompareTo(startNote) > 0 && startSelect.CompareTo(endNote) < 0 &&
                                 endSelect.CompareTo(endNote) > 0) isSelectionWithin = true;

                        if (isSelectionWithin)
                            selected.Add(a);
                    }
                }

                _selectedAnnotationCache = selected;
                return selected;
            }
            return _selectedAnnotationCache;
        }

        // TODO: move to ViewModel?
        private void RemoveBookmarksFromCurrentPage()
        {
            if (_viewModel == null) return;

            var toRemove = (from location in _viewModel.Model.UserMetadata.Bookmarks
                            let isBookmarkOnPage = BookViewer.IsLocationOnCurrentPage(location)
                            where ((bool?) isBookmarkOnPage).Value
                            select location).ToList();

            foreach (var location in toRemove)
                _viewModel.Model.UserMetadata.Bookmarks.Remove(location);

            _viewModel.RefreshBookmarks();
        }

        // TODO: move to ViewModel?
        private void SetBookmarkOnCurrentPage()
        {
            if (_viewModel == null) return;

            var b = new CourseLocation(BookViewer.CurrentLocation.Chapter, BookViewer.CurrentLocation.ChapterLocation);
            _viewModel.Model.UserMetadata.Bookmarks.Add(b);

            _viewModel.RefreshBookmarks();
        }

        #endregion Private Methods 

        #region CurrentPageHasBookmark
        
        /// <summary>
        /// CurrentPageHasBookmark Dependency Property
        /// </summary>
        public static readonly DependencyProperty CurrentPageHasBookmarkProperty =
            DependencyProperty.Register("CurrentPageHasBookmark", typeof (bool), typeof (ReadingView),
                                        new FrameworkPropertyMetadata(false,
                                                                      new PropertyChangedCallback(
                                                                          OnCurrentPageHasBookmarkChanged)));

        /// <summary>
        /// Gets or sets the CurrentPageHasBookmark property.  This dependency property 
        /// indicates ....
        /// </summary>
        public bool CurrentPageHasBookmark
        {
            get { return (bool) GetValue(CurrentPageHasBookmarkProperty); }
            set { SetValue(CurrentPageHasBookmarkProperty, value); }
        }

        /// <summary>
        /// Handles changes to the CurrentPageHasBookmark property.
        /// </summary>
        private static void OnCurrentPageHasBookmarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ReadingView)d).OnCurrentPageHasBookmarkChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the CurrentPageHasBookmark property.
        /// </summary>
        private void OnCurrentPageHasBookmarkChanged(DependencyPropertyChangedEventArgs e)
        {
            if (_isBookMarkFromMetadata) return;

            if ((bool)e.NewValue)
                SetBookmarkOnCurrentPage();
            else
                RemoveBookmarksFromCurrentPage();
        }
        
        #endregion

        public void PrepareNavigate()
        {
            LoadingIndicator.Visibility = Visibility.Collapsed;
            PercentLoadedTextBlock.Text = "0 %";
        }
    }
}